/* 
  泛型是可以在保证类型安全的前提下，让函数等与多种类型一起工作，从而实现复用，常用于：函数、接口、class  
*/

// 定义泛型函数
function id<Type>(value: Type): Type {
  return value;
}

// 用 number 类型使用泛型
const id1 = id<number>(11);
// 用 string 类型使用泛型
const id2 = id<string>("22");

//<number> 可以省略
let id3 = id(11);
let id4 = id('abc');

/* ********************** 泛型的类型约束 ************************** */
// 当想打印出value的length时，如果不加约束，传入的参数value很可能没有length属性，会报错
function idd<Type>(value: Type[]): Type[] {
  console.log(value.length);
  return value;
}

const idd1 = idd([123, 1234]);

// 用接口约束泛型
interface ILength {
  length: number
}

// 这里的extends不表示继承，表示为泛型添加约束
// 传入的参数只要有 length 属性即可（数组、字符串、有 length 属性的对象都可以）
function iddd<Type extends ILength>(value: Type): Type {
  console.log(value.length);
  return value;
}

const iddd1 = iddd([1, 2, 3]);
const iddd2 = iddd('abcc');
const iddd3 = iddd({ length: 10, name: "alice" });
const oddd4 = iddd(456);//error, 456 没有 length属性

/* ********************** 泛型的类型约束，类型变量之间的约束 ************************** */
// 第一个参数是泛型 Type，第二个参数是 obj 中的一个属性（第二个参数被第一个约束）
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key];
}

getProp({ name: 'alice', age: 20 }, "age");
getProp({ name: 'alice', age: 20 }, "name");

// 118.325 的属性中有 toFixed
getProp(118.325, "toFixed");
/* ********************** 泛型接口 ************************** */
/* 
  在接口名称后面添加 <类型变量>，那么这个接口就变成了泛型接口 
*/
interface IdFunc1<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}

// 接口中的类型变量对接口中所有其它可见，也就是接口中所有成员都可以使用类型变量
// 使用泛型接口时，需要显式地指定具体的类型 IdFunc1<number>
// 此时id方法的参数和返回值类型都是 number， ids 方法的返回值类型是 number[]
let obj: IdFunc1<number> = {
  id(value){
    return value;
  },
  ids(){return [1,2,3]}
}

